/*
  The pit.c file handles the PIT - "Programmable Interrupt Timer" on an Intel 386 like system.
  The PIC is the chip called 8253.
*/

#include <types.h>
#include <klib.h>
#include <pit.h>
#include <scheduler.h>
#include <globals.h>
#include <spinlock.h>
#include <panic.h>

// number of micro-seconds in a milli-second
#define TIME_US_PER_MS   1000L
// number of milli-seconds in a day
#define TIME_MS_PER_DAY  86400000L

// time keeping functions
volatile dword_t days,milli_seconds,micro_seconds;
spinlock_t time_mutex;

// internal functions prototypes
void pit_reset_time(void);

// Program PIT to generate interrupts at specified frequency.
void pit_set_frequency(int frequency)
  {
    unsigned int val;

    pit_reset_time();

    //val = ((1193180L + (unsigned int)frequency>>1) / (unsigned int)frequency); 

    val = (1193182L / (unsigned int)frequency); 
    // round up to try and amortize system delays
    // if the PIT is too fast, then increase "val"
    // if it's too slow, then decrease it
    //val-=6;

    /*
    const unsigned int base = 1193182L;
    word_t val;

    //val=(word_t)(base/frequency);
    val=(word_t)1193182;
    */

    /* channel 0, LSB/MSB, mode 3, binary */
    //outb(0x43, 0x36);	          
    outb(0x43, 0x34);	           // mode 2

    /* LSB */
    outb(0x40, val & 0x00FF);  
    /* MSB */
    outb(0x40, val >> 8);    
  }

void pit_reset_time(void)
{
  days=0;
  milli_seconds=0;
  micro_seconds=0;

  // initialize time mutex
  lock_initialize(&time_mutex);
}

void pit_get_time(dword_t *d,dword_t *ms,dword_t *us)
{
  int hwi;

  // acquire time mutex  
  hwi=lock_acquire_cli(&time_mutex);

  *d=days;
  *ms=milli_seconds;
  *us=micro_seconds;

  // release time mutex
  lock_release_sti(&time_mutex,hwi);
}

void pit_get_time_ms(qword_t *time_ms)
{
  dword_t d,ms,us;

  pit_get_time(&d,&ms,&us);

  *time_ms=(qword_t)TIME_MS_PER_DAY * d + ms;
}

void pit_isr(void)
{
  qword_t time_ms;
  int released;

  //int hwi;
  
  // acquire time mutex
  //hwi=lock_acquire_cli(&time_mutex);

  /*
  // number of elapsed micro-seconds at each interrupt
  micro_seconds += TIME_QUANTUM;
  
  // has one milli-second elapsed?
  while (micro_seconds >= TIME_US_PER_MS)
    {      
      micro_seconds -= TIME_US_PER_MS;
      milli_seconds++;
      
      // has one day elapsed?
      if (milli_seconds >= TIME_MS_PER_DAY)
	{
	  milli_seconds = 0;
	  days++;
	}
    }
  */

  //milli_seconds++;
  milli_seconds+=10;
      
  // has one day elapsed?
  if (milli_seconds >= TIME_MS_PER_DAY)
    {
      milli_seconds = 0;
      days++;
    }

  // get current time
  time_ms=(qword_t)TIME_MS_PER_DAY * days + milli_seconds;

  // release time mutex
  //lock_release_sti(&time_mutex,hwi);

  // release any periodic jobs that should be executed
  released=release_periodic(time_ms);

  // schedule only if we're coming from user mode
  // else the process is not really running
  // so don't decrease its time slice

  // kernel is being pre-empted by an interrupt so
  // task is not actually running
  if (!kernel_entering_count)
    {
      //printf("Proc: %s -- ts: %d\n",current_process->name,current_process->time_slice);
      
      // if it's a normal process... decrease its time slice
      if (current_process->type==TYPE_NORMAL)
	{
	  schedule();

	  /*
	  // update process' running-time information
	  current_process->time_slice--;
	  // need to reschedule?
	  //if (current_process->time_slice <= 0)
	  // need to reschedule? or any real time jobs pending?
	  if ((current_process->time_slice <= 0) || released)
	    schedule();  
	  */
	}
      else  // if it's a Real-Time process
	{
	  current_process->exec_remaining -= 10;

	  if (current_process->state == PS_RUNNING)
	    {
	      if (current_process->exec_remaining <= 0)
		{
		  printf("Process %s is trying to execute more than predicted.\n",current_process->name);
		  panic("Process cannot run any longer.");
		}
	      
	      if (current_process->absolute_deadline < time_ms)
		{
		  printf("MISSED DEADLINE: process %s\n",current_process->name);
		  printf("Current time: days=%d ms=%d\n",days,milli_seconds);
		  warning_message("\nMISSED DEADLINE\n");
		  panic("Missed deadline.");
		}
	    }
	}
    }
  
  // not here... because it would preempt jobs which have been started
  // against the EDF policy.
  // if we released any jobs...
  if (released)
    schedule();
}
